package jroguelike.main;

import aimozg.joglfw.GLColor;
import jroguelike.main.items.MeleeWeapon;
import jroguelike.utils.HashMultiMap;
import jroguelike.utils.MultiMap;
import org.jetbrains.annotations.Nullable;

import java.util.List;

import static jroguelike.main.Attribute.*;

public class Creature {

	// WORLD-related

	public final World world;
	private boolean isPc = false;
	public String name;
	public char glyph;
	public GLColor fg;

	private int x;
	private int y;
	private RLMap map = null;
	public ArtificialIntelligence ai;

	public boolean primaryWeaponActive = true;

	// PRIMARY RPG PARAMETERS

	private boolean dead = false;
	public int level = 1;
	public int actionPoints = 0;
	public int hitPts;
	public final int[] attributes = new int[COUNT];
	public final int[] skillBonuses = new int[Skill.COUNT];

	public GLColor getFg() {
		if (isPc) {
			RLItem armor = inventory.getFirst(InventorySlot.ARMOR);
			return (armor == null) ? fg : armor.color;
		}
		return fg;
	}

	public enum InventorySlot {
		PRIMARY_WEAPON, SECONDARY_WEAPON, ARMOR, BACKPACK
	}

	public final MultiMap<InventorySlot, RLItem> inventory = new HashMultiMap<>();

	// SECONDARY RPG PARAMETERS

	public int hitPtsMax() {
		return (attribute(ENDURANCE) + 1) * (10 + level);
	}

	public int speed() {
		return attribute(AGILITY) * 15 + 50 + level * 5 / 2;
	}

	public int sight() {
		return attribute(PERCEPTION) + 10;
	}

	// ACTION CALCULATIONS
	public int rollMeleeDamage(MeleeWeapon weapon) {
		if (weapon == null) {
			return world.rng.nextInt(attribute(STRENGTH)) + 1;
		}
		return weapon.damage.roll() + attribute(STRENGTH);
	}

	// HELPERS
	public int attribute(Attribute attr) {
		return attributes[attr.ordinal()];
	}

	public int skill(Skill skill) {
		return skillBonuses[skill.ordinal()] + 2 * attribute(skill.primaryAttribute) + attribute(LUCK);
	}

	public boolean isPc() {
		return isPc;
	}

	public boolean isDead() {
		return dead;
	}

	public RLItem getWeapon() {
		return inventory.getFirst(primaryWeaponActive ? InventorySlot.PRIMARY_WEAPON : InventorySlot.SECONDARY_WEAPON);
	}

	public List<RLItem> backpack() {
		return inventory.get(InventorySlot.BACKPACK);
	}

	// OTHER

	public void notifyDead(boolean newdead) {
		if (dead != newdead) {
			if (newdead) {
				addLog(GLColor.RED, "You die!..");
			} else {
				addLog(GLColor.CYAN, GLColor.WHITE, "You rise from dead!!!");
			}
			dead = newdead;
		}
	}

	public void notifyIsPc(boolean isPc) {
		this.isPc = isPc;
	}

	public Creature(World world) {
		this.world = world;
	}

	public Creature(Creature proto) {
		fg = proto.fg;
		glyph = proto.glyph;
		name = proto.name;
		world = proto.world;
		ai = proto.ai.makeClone(this);
		System.arraycopy(proto.attributes, 0, attributes, 0, COUNT);

		hitPts = hitPtsMax();
		actionPoints = 0;
		dead = false;
		isPc = false;
		map = null;
		x = 0;
		y = 0;
	}

	public int x() {
		return x;
	}

	public int y() {
		return y;
	}

	public int xy() {
		return map().xy(x, y);
	}

	public boolean canAct() {
		return actionPoints >= world.ACTION_COST;
	}

	public void moveTo(int tx, int ty) {
		assert map != null : "Local movement of unplaced";
		assert map.canMoveTo(tx, ty) : "Cannot move to";
		map.notifyCreatureMoved(this, x, y, tx, ty);
		x = tx;
		y = ty;
	}

	public void moveTo(@Nullable RLMap target, int tx, int ty) {
		if (map != null) {
			map.notifyCreatureMovedOut(this, x, y);
		}
		if (target != null) {
			assert target.canMoveTo(tx, ty) : "Cannot move to";
			target.notifyCreatureMovedIn(this, tx, ty);
		}
		map = target;
		x = tx;
		y = ty;
	}

	public RLMap map() {
		return map;
	}

	public void addLog(String text) {
		if (isPc) world.addLog(text);
	}

	public void addLog(String fmt, Object... args) {
		if (isPc) world.addLog(String.format(fmt, args));
	}

	public void addLog(GLColor fg, String text) {
		if (isPc) world.addLog(fg, text);
	}

	public void addLog(GLColor fg, String fmt, Object... args) {
		if (isPc) world.addLog(fg, String.format(fmt, args));
	}

	public void addLog(GLColor fg, GLColor bg, String text) {
		if (isPc) world.addLog(fg, bg, text);
	}
}
